package sg.edu.nus.iss.billing.controller;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import sg.edu.nus.iss.billing.BillingConstants;
import sg.edu.nus.iss.billing.dao.BaseDAO;
import sg.edu.nus.iss.billing.dao.DaoFactory;
import sg.edu.nus.iss.billing.exceptions.ControllerException;
import sg.edu.nus.iss.billing.model.user.User;
import sg.edu.nus.iss.billing.model.user.User.SystemAcctStatus;
import sg.edu.nus.iss.billing.model.user.User.SystemRole;

/**
 * @author Yeo Hee Ling, Saravana, Ananda
 */
public class UserController {

	private Properties properties;
	public UserController(){
		super();
		try {
			properties = new Properties();
			FileInputStream in = new FileInputStream(BillingConstants.PROPERTIES_ADMINISTRATOR);
			properties.load(in);
			in.close();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void createSystemUser(String personName, String username, String password, SystemRole systemRole) throws ControllerException {
		//Validate the input data first.
		//If there's any error, just throw an exception, to be caught and displayed at the GUI level
		String errorMsg = validateInput(personName, username, password, systemRole, true);		
		if(!errorMsg.isEmpty()){
			String displayMsg = properties.getProperty("error.message.createUnsuccessful.main");
			displayMsg += errorMsg.toString();
			throw new ControllerException(displayMsg);
		}
		
		try {
			BaseDAO<User> userDao = DaoFactory.getUserDAO(BillingConstants.DATASOURCE_USER);
			User user = new User(personName, username, password, systemRole, SystemAcctStatus.Active);
			userDao.saveElementByPK(user, "userName", user.getUserName());
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to create system user due to technical error. \nPlease contact your local administrator.");
		}
	}
	
	public void updateSystemUser(User user) throws ControllerException {
		//Validate the input data first.
		//If there's any error, just throw an exception, to be caught and displayed at the GUI level
		String errorMsg = validateInput(user.getPersonName(), user.getUserName(), user.getPassword(), user.getSystemRole(), false);		
		if(!errorMsg.isEmpty()){
			String displayMsg = properties.getProperty("error.message.updateUnsuccessful.main");
			displayMsg += errorMsg.toString();
			throw new ControllerException(displayMsg);
		}
		
		try {
			BaseDAO<User> userDao = DaoFactory.getUserDAO(BillingConstants.DATASOURCE_USER);
			userDao.saveElementByPK(user, "userName", user.getUserName());
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to update system user due to technical error. \nPlease contact your local administrator.");
		}
	}
	
	private String validateInput(String personName, String username, String password, SystemRole systemRole, boolean isCreate) throws ControllerException {
		StringBuffer errorMsg = new StringBuffer();
		
		//user name check 
		if(username == null || "".equals(username)) {
			errorMsg.append(properties.getProperty("error.message.usernameMandatory"));
		}
		else {
			if(!username.matches("^[a-zA-Z0-9]{3,15}$")) {
				errorMsg.append(properties.getProperty("error.message.usernameConstraint1"));
				errorMsg.append(properties.getProperty("error.message.usernameConstraint2"));
			}
			else {
				if(isCreate && isUserExist(username)) {
					errorMsg.append(properties.getProperty("error.message.usernameExist"));
				}
			}
		}
		
		//person name check
		if(personName == null || "".equals(personName)) {
			errorMsg.append(properties.getProperty("error.message.nameMandatory"));
		}
		
		//password check
		if(password == null || "".equals(password)) {
			errorMsg.append(properties.getProperty("error.message.passwordMandatory"));
		}
		else {
			if(!password.matches("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,20})")) {
				errorMsg.append(properties.getProperty("error.message.passwordConstraint1"));
				errorMsg.append(properties.getProperty("error.message.passwordConstraint2"));
			}
		}
		
		return errorMsg.toString();
	}
	
	public List<User> searchExistingUser(String personName, String username, SystemRole systemRole, SystemAcctStatus systemAcctStatus) throws ControllerException {
		List<User> matchedUsers = new ArrayList<User>();
		
		try {
			BaseDAO<User> userDao = DaoFactory.getUserDAO(BillingConstants.DATASOURCE_USER);
			
			List<User> users = userDao.getElementList();
			if(users != null && users.size() > 0) {
				Iterator<User> iter = users.iterator();
				while(iter.hasNext()) {
					boolean matchedPersonName = true;
					boolean matchedUsername = true;
					boolean matchedSystemRole = true;
					boolean matchedSystemAcctStatus = true;
					User user = iter.next();
					
					if(personName != null && personName.length() > 0) {
						int index = user.getPersonName().toUpperCase().indexOf(personName.toUpperCase());
						if(index == -1)
							matchedPersonName = false;
					}
					if(username != null && username.length() > 0) {
						int index = user.getUserName().toUpperCase().indexOf(username.toUpperCase());
						if(index == -1)
							matchedUsername = false;
					}
					if(systemRole != null) {
						if(user.getSystemRole() != systemRole)
							matchedSystemRole = false;
					}
					if(systemAcctStatus != null) {
						if(user.getSystemAcctStatus() != systemAcctStatus)
							matchedSystemAcctStatus = false;
					}
					
					if(matchedPersonName && matchedUsername && matchedSystemRole && matchedSystemAcctStatus)
						matchedUsers.add(user);
				}
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to search system user due to technical error. \nPlease contact your local administrator.");
		}
		return matchedUsers;
	}
	
	private boolean isUserExist(String username) throws ControllerException {
		boolean userExist = false;
		
		try {
			BaseDAO<User> userDao = DaoFactory.getUserDAO(BillingConstants.DATASOURCE_USER);
			
			List<User> users = userDao.getElementList();
			if(users != null && users.size() > 0) {
				Iterator<User> iter = users.iterator();
				while(iter.hasNext()) {
					User user = iter.next();
					
					if(username.equalsIgnoreCase(user.getUserName())) {
						userExist = true;
						break;
					}
				}
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			throw new ControllerException("Unable to create system user due to technical error. \nPlease contact your local administrator.");
		}
		return userExist;
	}
}
